package com.chenfan.mcn.event;

import com.chenfan.ccp.utils.TraceIdUtil;
import com.chenfan.mcn.config.UserVoContextHolder;
import com.chenfan.mcn.enums.LogBusinessTypeEnum;
import com.chenfan.mcn.extension.log.OperationTypeEnum;
import com.chenfan.common.extension.context.TraceContext;
import com.chenfan.common.extension.context.TraceContextHolder;
import com.chenfan.mcn.extension.mybatisplus.BaseAssembler;
import lombok.Getter;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.util.Date;

/**
 * 操作日志事件
 *
 * @author wulg
 * @date 2021-11-17
 **/
@Getter
public class OperateLogEvent<T extends BaseAssembler> {

    /**
     * traceId
     */
    private String traceId;

    /**
     * 行为名称
     */
    private String actionName;

    /**
     * 业务ID
     */
    private Long businessId;

    /**
     * 业务类型
     */
    private Integer businessType;

    /**
     * 行为备注
     */
    private String remark;

    /**
     * 操作类型
     */
    private Integer operationType;

    /**
     *  变更前对象
     */
    private T beforeValue;

    /**
     *  变更后对象
     */
    private T afterValue;

    /**
     * 操作时间
     */
    private Date operationTime;

    /**
     * 操作人id
     */
    private Long operationBy;

    /**
     * 操作人名称
     */
    private String operationName;

    public OperateLogEvent(final String traceId, final String actionName,
                           final Long businessId, final Integer businessType, final String remark,
                           final Integer operationType, final T beforeValue, final T afterValue,
                           final Date operationTime, final Long operationBy, final String operationName) {
        this.traceId = traceId;
        this.actionName = actionName;
        this.businessId = businessId;
        this.businessType = businessType;
        this.remark = remark;
        this.operationType = operationType;
        this.beforeValue = beforeValue;
        this.afterValue = afterValue;
        this.operationTime = operationTime;
        this.operationBy = operationBy;
        this.operationName = operationName;
    }

    /**
     * 构建方法
     * @param <T>
     * @return
     */
    public static <T extends BaseAssembler> OperateLogEventBuilder<T> builder() {
        return new OperateLogEventBuilder<T>();
    }

    /**
     * 操作日志构建者
     * @param <T>
     */
    public static class OperateLogEventBuilder<T extends BaseAssembler> {
        private Long businessId;
        private Integer businessType;
        private String actionName;
        private String remark;
        private Integer operationType;
        private T before;
        private T after;

        public OperateLogEventBuilder() {

        }

        public OperateLogEventBuilder<T> businessId(final Long businessId) {
            this.businessId = businessId;
            return this;
        }

        public OperateLogEventBuilder<T> businessType(final LogBusinessTypeEnum businessType) {
            this.businessType = businessType.getType();
            return this;
        }

        public OperateLogEventBuilder<T> actionName(final String actionName) {
            return this.actionName(true, actionName);
        }

        public OperateLogEventBuilder<T> actionName(boolean condition, final String actionName) {
            if(condition) {
                this.actionName = actionName;
            }
            return this;
        }
        public OperateLogEventBuilder<T> remark(final String remark) {
            this.remark = remark;
            return this;
        }

        public OperateLogEventBuilder<T> delete(final String remark) {
            this.remark = remark;
            this.operationType = OperationTypeEnum.DELETE.getType();
            return this;
        }

        public OperateLogEventBuilder<T> delete(String template, Object... args) {
            this.remark = String.format(template, args);
            this.operationType = OperationTypeEnum.DELETE.getType();
            return this;
        }

        public OperateLogEventBuilder<T> insert(final String remark) {
            this.remark = remark;
            this.operationType = OperationTypeEnum.INSERT.getType();
            return this;
        }

        public OperateLogEventBuilder<T> insert(String template, Object... args) {
            this.remark = String.format(template, args);
            this.operationType = OperationTypeEnum.INSERT.getType();
            return this;
        }

        public OperateLogEventBuilder<T> compare(final T before, final T after) {
            this.before = before;
            this.after = after;
            this.operationType = OperationTypeEnum.UPDATE.getType();
            return this;
        }

        public OperateLogEvent<T> build() {
            return this.build(null, null);
        }

        public OperateLogEvent<T> build(Long operationBy, String operationName) {
            Assert.notNull(businessId, "业务ID不能为空！日志构建失败！");
            Assert.notNull(operationType, "操作类型不能为空！日志构建失败！");
            if(StringUtils.isBlank(this.actionName)) {
                this.actionName = TraceContextHolder.get(TraceContext.ACTION_NAME);
            }
            return new OperateLogEvent<T>(
                    TraceIdUtil.getTraceId(),
                    this.actionName,
                    this.businessId,
                    this.businessType,
                    this.remark,
                    ObjectUtils.defaultIfNull(this.operationType, OperationTypeEnum.UPDATE.getType()),
                    this.before,
                    this.after,
                    new Date(),
                    ObjectUtils.defaultIfNull(operationBy, UserVoContextHolder.getUserId()),
                    ObjectUtils.defaultIfNull(operationName, UserVoContextHolder.getDefaultUserName()));
        }
    }
}
